package my.netty.channel;

import my.netty.channel.handler.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.ByteBuffer;

public class ChannelPipeline {

    private static final Logger log = LoggerFactory.getLogger(ChannelPipeline.class);

    private final Channel channel;
    private final AbstractChannelHandlerContext head;
    private final AbstractChannelHandlerContext tail;

    public ChannelPipeline(Channel channel) {
        this.channel = channel;
        head = new HeadContext(this);
        tail = new TailContext(this);
        head.next = tail;
    }

    public ChannelPipeline addFirst(ChannelHandler channelHandler) {
        synchronized (this) {
            AbstractChannelHandlerContext oldNext = head.next;
            AbstractChannelHandlerContext newNext = new DefaultChannelHandlerContext(channelHandler, this);
            head.next = newNext;
            newNext.prev = head;
            newNext.next = oldNext;
            oldNext.prev = newNext;
        }
        return this;
    }

    public final ChannelPipeline fireChannelRead(Object msg) {
        head.fireChannelRead(msg);
        return this;
    }

    public final void writeAndFlush(Object msg) throws IOException {
        tail.fireWriteAndFlush(msg);
    }

    static final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler {

        public HeadContext(ChannelPipeline pipeline) {
            super(pipeline);
        }

        @Override
        public ChannelHandler handler() {
            return this;
        }

        @Override
        public void writeAndFlush(AbstractChannelHandlerContext ctx, Object obj) throws IOException {
            if (obj instanceof ByteBuffer) {
                ((WritableChannel) pipeline().channel).writeAndFlush((ByteBuffer) obj);
            } else {
                ((WritableChannel) pipeline().channel).writeAndFlush(ByteBuffer.wrap(obj.toString().getBytes()));
            }
        }
    }

    static final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {

        public TailContext(ChannelPipeline pipeline) {
            super(pipeline);
        }

        @Override
        public ChannelHandler handler() {
            return this;
        }

        @Override
        public void channelRead(AbstractChannelHandlerContext ctx, Object msg) {
            try {
                log.debug("Discarded inbound message {} that reached at the tail of the pipeline. " +
                                "Please check your pipeline configuration.", msg);
            } finally {
                // 释放资源
//                ReferenceCountUtil.release(msg);
            }
        }
    }
}
